package test

import (
	"testing"
	"os"
	"io"
	"fmt"
	"bytes"
	"errors"
	"strconv"
	"path/filepath"
	"kumachan/standalone/rx"
	"kumachan/standalone/util/fatal"
	"kumachan/interpreter"
	"kumachan/interpreter/runtime/vm"
	"kumachan/interpreter/runtime/lib/env"
)


const failedToCompile = "failed to compile"

func getTestDirPath(t *testing.T, kind string) string {
	var exe_path, err = os.Executable()
	if err != nil { t.Fatal(err) }
	var project_path = filepath.Dir(filepath.Dir(exe_path))
	return filepath.Join(project_path, "test", kind)
}

func bufferFiles(in *bytes.Buffer, out *bytes.Buffer) (*os.File, *os.File, func()) {
	var sem = make(chan struct{}, 2)
	var stdin, stdin_write, err1 = os.Pipe()
	if err1 != nil { panic(err1) }
	var stdout_read, stdout, err2 = os.Pipe()
	if err2 != nil { panic(err2) }
	sem <- struct{}{}
	go (func() {
		_, err := io.Copy(stdin_write, in)
		if err != nil { panic(err) }
		err = stdin_write.Close()
		if err != nil { panic(err) }
		<- sem
	})()
	sem <- struct{}{}
	go (func() {
		_, err := io.Copy(out, stdout_read)
		if err != nil { panic(err) }
		err = stdout_read.Close()
		if err != nil { panic(err) }
		<- sem
	})()
	return stdin, stdout, func() {
		err := stdin.Close()
		if err != nil { panic(err) }
		err = stdout.Close()
		if err != nil { panic(err) }
		sem <- struct{}{}
		sem <- struct{}{}
	}
}

func expectStdIO(t *testing.T, path string, in string, expected_out string) {
	var p, err = interpreter.Compile(path)
	if err != nil {
		fatal.Log(err, failedToCompile, os.Stderr)
		t.Fatal(failedToCompile)
	}
	var in_buf = bytes.NewBufferString(in)
	var out_buf = new(bytes.Buffer)
	var stdin, stdout, std_close = bufferFiles(in_buf, out_buf)
	var test_env = env.NewDefaultEnvironment(nil)
	test_env.Stdin = rx.FileFrom(stdin)
	test_env.Stdout = rx.FileFrom(stdout)
	var options = &vm.Options { Environment: test_env }
	(func() {
		defer func() {
			var err = recover()
			if err != nil {
				t.Fatal(err)
			}
		}()
		interpreter.Run(p, options)
	})()
	std_close()
	var actual_out = out_buf.String()
	if actual_out != expected_out {
		t.Fatal(errors.New(fmt.Sprintf(
			"stdout not matching" + "\n" +
			"expected result:" + "\n" +
			"%s" +"\n" +
			"actual result:" + "\n" +
			"%s" + "\n",
			strconv.Quote(expected_out), strconv.Quote(actual_out),
		)))
	}
}


